#include <QFile>
#include <QTextStream>
#include <QMap>

#include <QtDebug>
#include <QtGui>
#include <QtSql>
#include <QtScript>
#include <QtXml>

#include "mainwindow.h"
#include "ScriptProcessor.h"
#include "DataProcessor.h"

MainWindow::MainWindow(QWidget *parent, Qt::WFlags flags)
: QMainWindow(parent, flags), outputXml(false), elfReader()
{
    ui.setupUi(this);
    
    connect(ui.actionOpenScriptFile, SIGNAL(triggered()), this, SLOT(openScriptFile()));
    connect(ui.actionOpenBufferFile, SIGNAL(triggered()), this, SLOT(openBufferFile()));
    connect(ui.actionOpenElfFile, SIGNAL(triggered()), this, SLOT(openElfFile()));
    connect(ui.actionProcess, SIGNAL(triggered()), this, SLOT(process()));
    connect(ui.actionOutputXml, SIGNAL(toggled(bool)), this, SLOT(setOutputXml(bool)));
    connect(ui.actionRender, SIGNAL(triggered()), this, SLOT(render()));
    connect(ui.actionRenderDummy, SIGNAL(triggered()), this, SLOT(renderDummy()));
    connect(ui.tableComboBox, SIGNAL(currentIndexChanged(QString)), this, SLOT(selectDataSourceTable(QString)));
    connect(ui.lineEdit, SIGNAL(returnPressed()), this, SLOT(filterDataSource()));
    connect(ui.actionSymbolResolution, SIGNAL(triggered()), this, SLOT(symbolResolution()));
    connect(ui.actionShowInspector, SIGNAL(triggered()), this, SLOT(showInspector()));
    connect(ui.actionShowColorPicker, SIGNAL(triggered()), this, SLOT(showColorPicker()));
    connect(ui.actionYAxisZoomIn, SIGNAL(triggered()), this, SLOT(doYAxisZoomIn()));
    connect(ui.actionYAxisZoomOut, SIGNAL(triggered()), this, SLOT(doYAxisZoomOut()));
    connect(ui.actionYAxisMoveDown, SIGNAL(triggered()), this, SLOT(doYAxisMoveDown()));
    connect(ui.actionYAxisMoveUp, SIGNAL(triggered()), this, SLOT(doYAxisMoveUp()));
    connect(ui.actionXAxisEvents, SIGNAL(triggered()), this, SLOT(doXAxisEvents()));
    connect(ui.actionXAxisTime, SIGNAL(triggered()), this, SLOT(doXAxisTime()));
    connect(ui.actionMutexLayerEnableAll, SIGNAL(triggered()), this, SLOT(doMutexLayerEnableAll()));
    connect(ui.actionMutexLayerDisableAll, SIGNAL(triggered()), this, SLOT(doMutexLayerDisableAll()));
    
    ui.actionOutputXml->toggle();
    
    ui.tableView->setModel(&model);
    
    symbolResolutionDialog = new SymbolResolutionDialog(this);
    symbolResolutionDialog->reader = &elfReader;
	
    inspectorDialog = new InspectorDialog(this);
    connect(inspectorDialog, SIGNAL(scaleChanged(float, float)), ui.tab_7->getCameraControllerPointer(), SLOT(setScaling(float, float)));
    connect(inspectorDialog, SIGNAL(moveChanged(float, float)), ui.tab_7->getCameraControllerPointer(), SLOT(move(float, float)));
    connect(inspectorDialog, SIGNAL(panChanged(float)), ui.tab_7->getCameraControllerPointer(), SLOT(panTo(float)));
    connect(inspectorDialog, SIGNAL(dummySearch(QString)), ui.tab_7, SLOT(dummySearch(QString)));
    connect(inspectorDialog, SIGNAL(clearSelectionOrSearch()), ui.tab_7, SLOT(clearSelectionOrSearch()));
    connect(inspectorDialog, SIGNAL(moveToPosition(float)), ui.tab_7->getCameraControllerPointer(), SLOT(moveToTraceHorizontalPos(float)));
	
    colorPickerDialog = new ColorPickerDialog(this);
    connect(colorPickerDialog, SIGNAL(colorsChanged()), ui.tab_7, SLOT(updateGL()));
	
    QMap<QString, QString> randomData;
    randomData["one"] = "onevalue";
    randomData["two"] = "twovalue";
    randomData["three"] = "threevalue";
    inspectorDialog->setData(randomData);
}

MainWindow::~MainWindow()
{
}

void MainWindow::openScriptFile()
{
    QString fileName = QFileDialog::getOpenFileName(this, tr("Open Script File"), ".");
    if (!fileName.isEmpty())
        scriptFilePath = fileName;
}

void MainWindow::openBufferFile()
{
    QString fileName = QFileDialog::getOpenFileName(this, tr("Open Events Buffer File"), ".");
    if (!fileName.isEmpty())
        bufferFilePath = fileName;
}

void MainWindow::openElfFile()
{
    QString fileName = QFileDialog::getOpenFileName(this, tr("Open ELF File"), ".");
    if (!fileName.isEmpty()) {
        elfFilePath = fileName;
        elfReader.load(elfFilePath);
    }
}

void MainWindow::selectDataSourceTable(const QString& table)
{
    model.setTable(table);
    model.setFilter("");
    model.select();
    
    ui.tableView->resizeRowsToContents();
    ui.tableView->resizeColumnsToContents();
}

void MainWindow::filterDataSource()
{
    model.setFilter(ui.lineEdit->text());
}

void MainWindow::process()
{
    ScriptContext context;
    context.bufferFilePath = bufferFilePath;
    context.elfFilePath = elfFilePath;
    context.swapBytes = true;
    
    context.dataSource = &dataSource;
    
    ScriptProcessor proc(&context);
    proc.loadScript(scriptFilePath);
//    proc.print();
    proc.process();
    
    DataProcessor::instance().process();
    
    ui.tableComboBox->clear();
    ui.tableComboBox->addItems(dataSource.tables.keys());
    if (dataSource.tables.keys().size() > 0)
        selectDataSourceTable(dataSource.tables.keys().at(0));
    
    renderMutex();
    renderThreads();
	
	updateVisualizer();
}

void MainWindow::setOutputXml(bool flag)
{
    outputXml = flag;
}

void MainWindow::doYAxisZoomIn()
{
  if (ui.tab_7->hasFocus())
  {
    ui.tab_7->getCameraControllerPointer()->move(0, 0, -1);
  }
}

void MainWindow::doYAxisZoomOut()
{
  if (ui.tab_7->hasFocus())
  {
    ui.tab_7->getCameraControllerPointer()->move(0, 0, +1);
  }
}

void MainWindow::doYAxisMoveUp()
{
  if (ui.tab_7->hasFocus())
  {
    ui.tab_7->getCameraControllerPointer()->move(0, +1, 0);
  }
}

void MainWindow::doYAxisMoveDown()
{
  if (ui.tab_7->hasFocus())
  {
    ui.tab_7->getCameraControllerPointer()->move(0, -1, 0);
  }
}

void MainWindow::doXAxisEvents()
{
  if (ui.tab_7->hasFocus())
  {
    if (ui.tab_7->getScene())
    {
      // only do something if we are currently timeBased
      if (ui.tab_7->getScene()->isTimeBased() == true)
      {
        // change the render widget
        ui.tab_7->getScene()->switchView();
        ui.tab_7->updateGL();
        // change the Q keyboard shortcut
        ui.actionXAxisEvents->setShortcut(NULL);
        ui.actionXAxisTime->setShortcut(Qt::Key_Q);
      }
    }
  }
}

void MainWindow::doXAxisTime()
{
  if (ui.tab_7->hasFocus())
  {
    if (ui.tab_7->getScene())
    {
      // only do something if we are currently eventBased
      if (ui.tab_7->getScene()->isTimeBased() == false)
      {
        // change the render widget
        ui.tab_7->getScene()->switchView();
        ui.tab_7->updateGL();
        // change the Q keyboard shortcut
        ui.actionXAxisEvents->setShortcut(Qt::Key_Q);
        ui.actionXAxisTime->setShortcut(NULL);
      }
    }
  }
}

void MainWindow::doMutexLayerEnableAll()
{
  if (ui.tab_7->hasFocus())
  {
    if (ui.tab_7->getScene())
    {
      ui.tab_7->getScene()->setDrawMutexesForAll(true);
      ui.tab_7->updateGL();
    }
  }
}

void MainWindow::doMutexLayerDisableAll()
{
  if (ui.tab_7->hasFocus())
  {
    if (ui.tab_7->getScene())
    {
      ui.tab_7->getScene()->setDrawMutexesForAll(false);
      ui.tab_7->updateGL();
    }
  }
}

void MainWindow::renderMutex()
{
}

void MainWindow::renderThreads()
{
}

bool dataItemComesBefore(const DataItem* left, const DataItem* right)
{
	return left->eventNr <= right->eventNr;
}

void MainWindow::updateVisualizer()
{
	Scene* scene = new Scene(&dataSource);
	Trace& eventBasedTrace = scene->getEventBasedTrace();
	Trace& timeBasedTrace = scene->getTimeBasedTrace();
	
	// Register the threads and the mutexes
	DataProcessor& processor = DataProcessor::instance();
	QListIterator<ThreadMapping*> threadsIter(processor.getThreads());
	QMap<int, Thread*> threadMap;
	QMap<int, Thread*> timeThreadMap;
	while (threadsIter.hasNext()) {
		ThreadMapping* threadMapping = threadsIter.next();
		
//		qDebug() << "adding thread" << threadMapping->name << "with id " << threadMapping->id;
		
		if (threadMapping != processor.getInterruptThreadMap()) {
			// Create a thread item for the id
			Thread* thread = new Thread(threadMapping->id, threadMapping->name);
			Thread* timeThread = new Thread(threadMapping->id, threadMapping->name);
			threadMap[threadMapping->id] = thread;
			timeThreadMap[threadMapping->id] = timeThread;
			eventBasedTrace.addThread(thread);
			timeBasedTrace.addThread(timeThread);
			
			QVectorIterator<DataMutexItem*> mutexesIter(processor.getMutexes(threadMapping->id));
			while (mutexesIter.hasNext()) {
				DataMutexItem* mutexItem = mutexesIter.next();
				Mutex* mutex = new Mutex(mutexItem->symbol, mutexItem->eventLocation, mutexItem->eventEnd(), mutexItem->level);
				thread->addMutex(mutex);
				
				Mutex* timeMutex = new Mutex(mutexItem->symbol, processor.timeForEvent(mutexItem->eventLocation), processor.timeForEvent(mutexItem->eventEnd()), mutexItem->level);
				timeThread->addMutex(timeMutex);
				
	//			qDebug() << "adding mutex" << mutexItem->symbol << "with length " << mutexItem->eventLength << " level " << mutexItem->level;
			}
		}
	}
	
	// Register the thread states
	QList<DataEventItem*> events;
	QSetIterator<DataItem*> allItemsIter(processor.itemsInEventRange(0, processor.totalEvents()));
	while (allItemsIter.hasNext()) {
		DataItem* item = allItemsIter.next();
		
    if ( (reinterpret_cast<void*>(item) == (void*) 0xFDFDFDFD)
      || (item == NULL) 
       )
    {
      qDebug() << "allItemsIter.next() returned garbage";
      continue;
    }

		if (item && item->type() == DataThreadStateItemType) {
			DataThreadStateItem* threadStateItem = (DataThreadStateItem*)item;
			
			// Check if the state belongs to the interrupt handling
			if (threadStateItem->threadMapping == processor.getInterruptThreadMap()) {
				eventBasedTrace.getInterrupts().addState(new State(threadStateItem->eventLocation, threadStateItem->eventEnd(), threadStateItem->state));
				timeBasedTrace.getInterrupts().addState(new State(processor.timeForEvent(threadStateItem->eventLocation), processor.timeForEvent(threadStateItem->eventEnd()), threadStateItem->state));
			}
			else {
				Thread* thread = threadMap[threadStateItem->threadMapping->id];
				if (thread)
					thread->addState(new State(threadStateItem->eventLocation, threadStateItem->eventEnd(), threadStateItem->state));
				Thread* timeThread = timeThreadMap[threadStateItem->threadMapping->id];
				if (timeThread)
					timeThread->addState(new State(processor.timeForEvent(threadStateItem->eventLocation), processor.timeForEvent(threadStateItem->eventEnd()), threadStateItem->state));
			}
			
		}
		else if (item && item->type() == DataEventItemType) {
			DataEventItem* eventItem = (DataEventItem*)item;
			events.append(eventItem);
		}
	}
	
	// Register the events
	qSort(events.begin(), events.end(), dataItemComesBefore);
	QListIterator<DataEventItem*> allEventsIter(events);
	while (allEventsIter.hasNext()) {
		DataEventItem* event = allEventsIter.next();
		eventBasedTrace.addEvent(new Event(event->eventClass, event->eventType));
		timeBasedTrace.addEvent(new Event(event->eventClass, event->eventType));
	}
	
	ui.tab_7->setScene(scene);
	connect(colorPickerDialog, SIGNAL(colorsChanged()), scene, SLOT(updateColors()));
	connect(inspectorDialog, SIGNAL(focusSelection()), scene, SLOT(focusSelection()));
	connect(inspectorDialog, SIGNAL(moveToPrevious()), scene, SLOT(moveToPrevious()));
	connect(inspectorDialog, SIGNAL(moveToNext()), scene, SLOT(moveToNext()));

	connect(scene, SIGNAL(selectionDescriptionChanged(const QMap<QString, QString>&)), inspectorDialog, SLOT(updateInspectorData(const QMap<QString, QString>&)));
}

void MainWindow::render()
{
}

void MainWindow::renderDummy()
{
    // Render some dummy data in the visualizer
    Scene* renderWidgetScene = new Scene(&dataSource);
    renderWidgetScene->createDummy();
    ui.tab_7->setScene(renderWidgetScene);
    connect(colorPickerDialog, SIGNAL(colorsChanged()), renderWidgetScene, SLOT(updateColors()));
	connect(inspectorDialog, SIGNAL(focusSelection()), renderWidgetScene, SLOT(focusSelection()));
	connect(inspectorDialog, SIGNAL(moveToPrevious()), renderWidgetScene, SLOT(moveToPrevious()));
	connect(inspectorDialog, SIGNAL(moveToNext()), renderWidgetScene, SLOT(moveToNext()));
	
	connect(renderWidgetScene, SIGNAL(selectionDescriptionChanged(const QMap<QString, QString>&)), inspectorDialog, SLOT(updateInspectorData(const QMap<QString, QString>&)));
    
    qDebug() << "rendered visualizer";
}

void MainWindow::symbolResolution()
{
    symbolResolutionDialog->show();
}

void MainWindow::showInspector()
{
    inspectorDialog->show();
}

void MainWindow::showColorPicker()
{
    colorPickerDialog->show();
}
